﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace DTO
{
    public class Concept
    {
        #region Attributes
        int id;
        Vector featureVector;
        List<Vector> webpageInstanceVector;
        List<string> candidates;
        #endregion

        #region Properties
        public List<string> Candidates
        {
            get { return candidates; }
            set { candidates = value; }
        }
        
        public Vector FeatureVector
        {
            get
            {
                return featureVector;
            }
            set
            {
                featureVector = value;
            }
        }

        public List<Vector> WebpageInstanceVector
        {
            get
            {
                return webpageInstanceVector;
            }
            set
            {
                webpageInstanceVector = value;
            }
        }

        public int ID
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        } 
        #endregion

        #region Constructor
        public Concept()
        {
            webpageInstanceVector = new List<Vector>();
        } 
        #endregion

        #region Methods
        /// <summary>
        /// Convert list of clustered vectors to list of concepts then add instances into each concept.
        /// </summary>
        /// <param name="vectorList">List of clustered vectors</param>
        /// <returns>List of concepts with their instances</returns>
        public static List<Concept> FromVectorsToConcept(List<Vector> vectorList)
        {
            Dictionary<int, Concept> conceptDict = new Dictionary<int, Concept>();
            foreach (Vector curVector in vectorList)
            {
                int clusterID = curVector.ClusterNumber;
                if (conceptDict.ContainsKey(clusterID) == true)
                {
                    conceptDict[clusterID].WebpageInstanceVector.Add(curVector);
                }
                else
                {
                    Concept curConcept = new Concept();
                    curConcept.ID = clusterID;
                    curConcept.WebpageInstanceVector.Add(curVector);
                    conceptDict.Add(clusterID, curConcept);
                }
            }
            List<Concept> conceptList = conceptDict.Values.ToList();
            return conceptList;
        }

        /// <summary>
        /// This method read a file generated by clustering tool that contains all clusters.
        /// </summary>
        /// <param name="pagesVectorList">List of all vectors</param>
        /// <param name="filePath">The file path of the clustered file</param>
        public static void FromClusterFile(List<Vector> pagesVectorList, string filePath)
        {
            FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(fs);
            int vectorNumber = 0;
            try
            {
                while (sr.Peek() >= 0)
                {
                    string line = sr.ReadLine();
                    pagesVectorList[vectorNumber].ClusterNumber = Int32.Parse(line);
                    vectorNumber++;
                }
            }
            finally
            {
                sr.Close();
                fs.Close();
            }
            sr.Close();
            fs.Close();
        }

        /// <summary>
        /// Convert feature vector of this concept
        /// </summary>
        /// <param name="FeatureVector"></param>
        /// <returns></returns>
        public static string FVectorToString(Vector FeatureVector)
        {
            return FeatureVector.ToString();
            //string result = string.Empty;
            //foreach (double i in FeatureVector.Value)
            //{
            //    result += Convert.ToString(i) + " ";
            //}
            //return result;
        }

        ///// <summary>
        ///// Export list of concepts to file: concept # + list of instances: webpageID + URL + title
        ///// </summary>
        ///// <param name="conceptList">List of concepts</param>
        ///// <param name="allWebpagesList">List of all webpages</param>
        ///// <param name="filePath">The path (with name) of the exported file</param>
        //public static void ToConceptFile(List<Concept> conceptList, string filePath, List<Webpage> allWebpagesList)
        //{
        //    Dictionary<int, Webpage> allWebpagesDict = new Dictionary<int, Webpage>();
        //    // make webpage dictionary
        //    foreach (Webpage curWebpage in allWebpagesList)
        //    {
        //        allWebpagesDict.Add(curWebpage.ID, curWebpage);
        //    }


        //    FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
        //    StreamWriter sw = new StreamWriter(fs);
        //    foreach (Concept curConcept in conceptList)
        //    {
        //        sw.WriteLine("\n---------------------------------------Concept #" + curConcept.ID);

        //        foreach (Vector curVector in curConcept.WebpageInstanceVector)
        //        {
        //            sw.WriteLine(curVector.WebpageID);
        //            sw.WriteLine(allWebpagesDict[curVector.WebpageID].URL);
        //            sw.WriteLine(allWebpagesDict[curVector.WebpageID].Title);
        //            sw.WriteLine();
        //        }
        //        sw.WriteLine();
        //    }
        //    sw.Close();
        //    fs.Close();
        //}

        /// <summary>
        /// Export list of concepts to XML file
        /// </summary>
        /// <param name="conceptList">List of concepts</param>
        /// <param name="allWebpagesList">List of all webpages</param>
        /// <param name="filePath">The path (with name) of the exported file</param>
        public static void ToConceptFile(List<Concept> conceptList, string filePath, List<Webpage> allWebpagesList)
        {
            Dictionary<int, Webpage> allWebpagesDict = new Dictionary<int, Webpage>();
            // make webpage dictionary
            foreach (Webpage curWebpage in allWebpagesList)
            {
                allWebpagesDict.Add(curWebpage.ID, curWebpage);
            }

        
            XmlDocument xmlDoc = new XmlDocument();
            XmlElement xmlElementRoot = xmlDoc.CreateElement("concept_regconition");
            xmlDoc.AppendChild(xmlElementRoot);

            foreach (Concept curConcept in conceptList)
            {
                // <concept id=" " instancesNum=" ">
                XmlElement xmlElementConcept = xmlDoc.CreateElement("concept");
                XmlAttribute xmlConceptAttribute = xmlDoc.CreateAttribute("id");
                xmlConceptAttribute.Value = curConcept.ID.ToString();
                xmlElementConcept.Attributes.Append(xmlConceptAttribute);
                
                xmlConceptAttribute = xmlDoc.CreateAttribute("instancesNum");
                xmlConceptAttribute.Value = curConcept.WebpageInstanceVector.Count.ToString();
                xmlElementConcept.Attributes.Append(xmlConceptAttribute);
                
                // <featureVector>Feature Vector Value</featureVector>
                XmlElement xmlFeatureVector = xmlDoc.CreateElement("featureVector");
                xmlFeatureVector.InnerText = curConcept.FeatureVector.ToString();
                xmlElementConcept.AppendChild(xmlFeatureVector);

                XmlElement xmlInstances = xmlDoc.CreateElement("instances");

                foreach (Vector curVector in curConcept.WebpageInstanceVector)
                {
                    // <webpage id="" title="" URL="">
                    XmlElement xmlWebpage = xmlDoc.CreateElement("webpage");
                    XmlAttribute xmlWebpageAttribute = xmlDoc.CreateAttribute("id");
                    xmlWebpageAttribute.Value = curVector.WebpageID.ToString();
                    xmlWebpage.Attributes.Append(xmlWebpageAttribute);

                    xmlWebpageAttribute = xmlDoc.CreateAttribute("title");
                    xmlWebpageAttribute.Value = allWebpagesDict[curVector.WebpageID].Title;
                    xmlWebpage.Attributes.Append(xmlWebpageAttribute);

                    xmlWebpageAttribute = xmlDoc.CreateAttribute("URL");
                    xmlWebpageAttribute.Value = allWebpagesDict[curVector.WebpageID].URL;
                    xmlWebpage.Attributes.Append(xmlWebpageAttribute);

                    xmlWebpage.InnerText = curVector.ToString();

                    xmlInstances.AppendChild(xmlWebpage);
                }
                xmlElementConcept.AppendChild(xmlInstances);

                XmlElement xmlCandidates = xmlDoc.CreateElement("candidates");

                foreach (string curCandidate in curConcept.Candidates)
                {
                    // <candidate>Candidate Text</candidate>
                    XmlElement xmlCandidate = xmlDoc.CreateElement("candidate");
                    xmlCandidate.InnerText = curCandidate;
                    xmlCandidates.AppendChild(xmlCandidate);
                }
                xmlElementConcept.AppendChild(xmlCandidates);

                xmlElementRoot.AppendChild(xmlElementConcept);
            }

            try
            {
                xmlDoc.Save(filePath);
            }
            catch
            {
                
            }
        }
        
        /// <summary>
        /// Import list of concepts from XML file
        /// </summary>
        /// <param name="filePath">The path (with name) of the imported file</param>
        /// <returns>The list of concepts imported from the xml file</returns>
        public static List<Concept> FromConceptFile(string filePath)
        {
            List<Concept> conceptList = new List<Concept>();
            XmlDocument xmlDoc = new XmlDocument();
            try
            {
                xmlDoc.Load(filePath);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            XmlNodeList xmlConceptList = xmlDoc.GetElementsByTagName("concept");
            foreach (XmlNode curConceptNode in xmlConceptList)
            {
                Concept curConcept = new Concept();
                curConcept.Candidates = new List<string>();
                curConcept.WebpageInstanceVector = new List<Vector>();

                curConcept.ID = Int32.Parse(curConceptNode.Attributes.GetNamedItem("id").Value);
                Vector tempVector = new Vector();
                // get feature vector of this concept
                curConcept.FeatureVector = Vector.FromString(curConceptNode.ChildNodes[0].InnerText);
                // get instances
                XmlNode instancesNode = curConceptNode.ChildNodes[1];
                for (int i = 0; i < instancesNode.ChildNodes.Count; i++)
                {
                    tempVector = new Vector();
                    tempVector = Vector.FromString(instancesNode.ChildNodes[i].InnerText);
                    tempVector.WebpageID = Int32.Parse(instancesNode.ChildNodes[i].Attributes.GetNamedItem("id").Value);
                    tempVector.ClusterNumber = curConcept.ID;
                    curConcept.WebpageInstanceVector.Add(tempVector);
                }
                // get candidates
                XmlNode candidatesNode = curConceptNode.ChildNodes[2];
                for (int i = 0; i < candidatesNode.ChildNodes.Count; i++)
                {
                    curConcept.Candidates.Add(candidatesNode.ChildNodes[i].InnerText);
                }
                conceptList.Add(curConcept);
            }
            return conceptList;
        }

        /// <summary>
        /// Export list of concepts along with the candidate's name list 
        /// </summary>
        /// <param name="conceptList">List of concepts</param>
        /// <param name="allKeyword">List of all keyword used</param>
        /// <param name="filePath">The path (with name) of the exported file</param>
        // allKeyword day la allKeyword da duoc su dung trong viec tao ra cac vector
        public static void ToConceptCandidateFile(List<Concept> conceptList, List<Keyword> allKeyword, string filePath)
        {
            FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
            StreamWriter sw = new StreamWriter(fs);
            int count = 0;
            foreach (Concept curConcept in conceptList)
            {
                sw.WriteLine("Concept #" + curConcept.ID);
                List<Keyword> curConceptKeywordList = new List<Keyword>();
                // list keyword duoc cho vao ket hop voi vector dac trung de rut ra cac candidate roi sau do sort lai list nay
                curConceptKeywordList = Vector.FeatureVectorToTerms(curConcept.FeatureVector, allKeyword);
                foreach (Keyword curKeyword in curConceptKeywordList)
                {
                    // doc tu file cac term da duoc su dung phai xuat ra dc id, text va cout luon
                    sw.Write(curKeyword.KeywordText + "|");
                }
                sw.WriteLine();
                Console.WriteLine(count++);
            }
            sw.Close();
            fs.Close();
        }


        public static void GetCandidates(List<Concept> conceptList, List<Keyword> allTerms)
        {
            foreach (Concept curConcept in conceptList)
            {
                curConcept.Candidates = new List<string>();
                List<Keyword> curConceptKeywordList = new List<Keyword>();
                // list keyword duoc cho vao ket hop voi vector dac trung de rut ra cac candidate roi sau do sort lai list nay
                curConceptKeywordList = Vector.FeatureVectorToTerms(curConcept.FeatureVector, allTerms);
                foreach (Keyword curKeyword in curConceptKeywordList)
                {
                    curConcept.Candidates.Add(curKeyword.KeywordText);
                }
            }
        }

        /// <summary>
        /// This methods convert list of concepts to a dictionary of webpageID-ConceptID
        /// </summary>
        /// <param name="conceptList">List of input concepts.</param>
        /// <returns>Dictionary [webpageID, conceptID] </returns> 
        public static Dictionary<int, int> ConceptToWebpageConceptDict(List<Concept> conceptList)
        {
            Dictionary<int, int> resultDict = new Dictionary<int, int>();

            foreach (Concept curConcept in conceptList)
            {
                foreach (Vector curVector in curConcept.WebpageInstanceVector)
                {
                    if (resultDict.ContainsKey(curVector.WebpageID) == false)
                    {
                        resultDict.Add(curVector.WebpageID, curConcept.ID);
                    }    
                }                
            }

            return resultDict;
        }

        /// <summary>
        /// This method merges the list of concepts
        /// </summary>
        /// <param name="conceptList">The original list of concepts</param>
        /// <param name="minimumNumberOfConceptInstances">The minimum of instances in a concept in order to used for merging</param>
        /// <param name="epsilonCosine">The threshold of the cosine between 2 vectors</param>
        /// <returns>The merged list of concepts</returns>
        public static List<Concept> MergeConcept(List<Concept> conceptList, int minimumNumberOfConceptInstances, double epsilonCosine)
        {
            List<Concept> newConceptList = new List<Concept>();
            HashSet<int> usedConcept = new HashSet<int>();

            foreach (Concept curConcept in conceptList)
            {
                // the concept with small amount of instances will not be considered
                if (curConcept.WebpageInstanceVector.Count > minimumNumberOfConceptInstances)
                {
                    newConceptList.Add(curConcept);
                }
            }

            for (int i = 0; i < newConceptList.Count - 1; i++)
            {
                usedConcept.Add(i);
                for (int j = i + 1; j < newConceptList.Count; j++)
                {
                    if (Vector.Cosine(newConceptList[i].FeatureVector, newConceptList[j].FeatureVector) > epsilonCosine)

                        usedConcept.Add(j);
                }
            }

            return newConceptList;
        } 
        #endregion
    }
}
